<?php
/**
 * 
 * Application Front Controller
 * 
 * @category Abovo
 * 
 * @package Abovo
 * 
 * @author Antti Holvikari <anttih@gmail.com>
 * 
 * @license http://opensource.org/licenses/gpl-license.php GPL
 *
 */

/**
 * 
 * Application Front Controller
 * 
 * @category Abovo
 * 
 * @package Abovo
 * 
 */
class Abovo_Controller_Front extends Solar_Base {
    
    /**
     * 
     * Config keys
     * 
     * `classes`:
     * (array) Class stack
     * 
     * `default`:
     * (string) Default app class name
     * 
     * @var array
     * 
     */
    protected $_Abovo_Controller_Front = array(
        'classes' => array('Abovo_App'),
        'default' => 'Abovo_App_System_User',
    );
    
    /**
     * 
     * The default page name when none is specified.
     * 
     * @var array
     * 
     */
    protected $_default;
    
    /**
     * 
     * Stack of page-controller class prefixes.
     * 
     * @var Solar_Class_Stack
     * 
     */
    protected $_stack;
    
    /**
     * 
     * Constructor.
     * 
     * @param array $config User-provided configuration values.
     * 
     */
    public function __construct($config)
    {
        // do the "real" construction
        parent::__construct($config); 
        
        // set convenience vars from config
        $this->_default = $this->_config['default'];
        
        // set up a class stack for finding apps
        $this->_stack = Solar::factory('Solar_Class_Stack');
        $this->_stack->add($this->_config['classes']);
    }
    
    /**
     * 
     * Fetches the output of a module/app/action/info specification URI.
     * 
     * If module/app is not found redirect to default app.
     * 
     * @param Solar_Uri_Action|string $spec An action URI for the front
     * controller.  E.g., 'module/app/action/pmjones/php+blog?page=2'.
     * 
     * @return string The output of the page action.
     * 
     */
    public function fetch($spec = null)
    {
        // default to current URI
        $uri = Solar::factory('Solar_Uri_Action');
        
        // override current URI with user spec
        if (is_string($spec)) {
            $uri->set($spec);
        }
        
        if ($spec instanceof Solar_Uri_Action) {
            // a URI was passed directly
            $uri = $spec;
        } else {
            // user spec is a URI string; if empty, is the current URI
            $uri = Solar::factory('Solar_Uri_Action', array(
                'uri' => (string) $spec,
            ));
        }
        
        // take the page name off the top of the path and try to get a
        // controller class from it.
        $class = false;
        
        // path needs to have at least 2 elements; module and page
        if (count($uri->path) >= 2) {
            // take module and page name (app)
            $module = array_shift($uri->path);
            $page   = array_shift($uri->path);
            
            // is this combination in the class stack
            if (! $class = $this->_getPageClass($module, $page)) {
                // ...no, so put them back
                array_unshift($uri->path, $page);
                array_unshift($uri->path, $module);
            }
        }
        
        // did we get a class?
        if (! $class) {
            // ...no, use default
            $file = str_replace('_', DIRECTORY_SEPARATOR, $this->_default) . '.php';
            // is it valid?
            if (Solar::fileExists($file)) {
                $class = $this->_default;
            }
        }
        
        // last chance: do we have a class yet?
        if (! $class) {
            return $this->_notFound();
        }
        
        // instantiate the controller class and fetch its content
        $obj = Solar::factory($class);
        // @todo this waits for 0.29
        //$obj->setFrontController($this);
        return $obj->fetch($uri);
    }
    
    /**
     * 
     * Displays the output of an page/action/info specification URI.
     * 
     * @param string $spec A page/action/info spec for the front
     * controller. E.g., 'bookmarks/user/pmjones/php+blog?page=2'.
     * 
     * @return string The output of the page action.
     * 
     */
    public function display($spec = null)
    {
        echo $this->fetch($spec);
    }
    
    /**
     * 
     * Finds the page-controller class name from a page name.
     * 
     * @param string $module Abovo module name
     * 
     * @param string $page page name
     * 
     * @return string The related page-controller class picked from
     * the routing, or from the list of available classes.  If not found,
     * returns false.
     * 
     */
    protected function _getPageClass($module, $page)
    {
        
        // try to find a matching class
        $module = str_replace('-',' ', $module);
        $module = str_replace(' ', '', ucwords(trim($module)));
        
        // try to find a matching class
        $page = str_replace('-',' ', $page);
        $page = str_replace(' ', '', ucwords(trim($page)));
        
        // complete app name
        $app = $module . '_' . $page;
        
        // try to find it
        $class = $this->_stack->load($app, false);
        
        return $class;
    }
    
    /**
     * 
     * Executes when fetch() cannot find a related page-controller class.
     * 
     * Generates an "HTTP 1.1/404 Not Found" status header and returns a
     * short HTML page describing the error.
     * 
     * @return string
     * 
     */
    protected function _notFound()
    {
        $content = "<html><head><title>Not Found</title>"
                 . "<body><h1>404 Not Found</h1><p>"
                 . htmlspecialchars("Page not found.")
                 . "</p></body></html>";
        
        $response = Solar::factory('Solar_Http_Response');
        $response->setStatusCode(404);
        $response->content = $content;
        
        return $response;
    }
}

